using Gj;
using Unity.Burst;
using Unity.Burst.Intrinsics;
using Unity.Collections;
using Unity.Entities;

[WorldSystemFilter(WorldSystemFilterFlags.Disabled)]
[UpdateInGroup(typeof(PresentationSystemGroup))]
[UpdateAfter(typeof(BeginPresentationEntityCommandBufferSystem))]
public partial class MoveSystemLater : SystemBase
{
    private EntityQuery actionFrozenGroup;
    private EntityQuery actionHoldGroup;

    private BeginInitializationEntityCommandBufferSystem CommandBufferSystem;
    private EntityQuery moveHoldHitGroup;

    protected override void OnCreate()
    {
        CommandBufferSystem = World.GetOrCreateSystemManaged<BeginInitializationEntityCommandBufferSystem>();
        // 冻结对象
        actionFrozenGroup = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadOnly<ActionFrozenData>()
            }
        });
        actionHoldGroup = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadOnly<ActionHoldData>()
            }
        });
        moveHoldHitGroup = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadOnly<MoveHoldData>(),
                ComponentType.ReadWrite<CastHitsData>()
            },
            None = new[]
            {
                ComponentType.ReadWrite<CastColliderData>()
            }
        });
    }

    protected override void OnUpdate()
    {
        var inputDeps = Dependency;
        var time = SystemAPI.Time.ElapsedTime;

        var commandBuffer = CommandBufferSystem.CreateCommandBuffer().AsParallelWriter();
        var EntityType = GetEntityTypeHandle();
        var MoveHoldDataType = GetComponentTypeHandle<MoveHoldData>();
        var ActionHoldDataType = GetComponentTypeHandle<ActionHoldData>();
        var ActionFrozenDataType = GetComponentTypeHandle<ActionFrozenData>();
        var EntityDataFromEntity = GetComponentLookup<EntityData>();
        var CastHitsDataType = GetBufferLookup<CastHitsData>();

        var frozenJob = new FrozenJob
        {
            CommandBuffer = commandBuffer,
            ActionFrozenDataType = ActionFrozenDataType,
            EntityType = EntityType,
            time = time
        };
        inputDeps = frozenJob.Schedule(actionFrozenGroup, inputDeps);

        var holdJob = new HoldJob
        {
            CommandBuffer = commandBuffer,
            ActionHoldDataType = ActionHoldDataType,
            EntityType = EntityType
        };
        inputDeps = holdJob.Schedule(actionHoldGroup, inputDeps);

        var moveHoldHitJob = new MoveHoldHitJob
        {
            CommandBuffer = commandBuffer,
            CastHitsDataType = CastHitsDataType,
            EntityDataFromEntity = EntityDataFromEntity,
            MoveHoldDataType = MoveHoldDataType,
            EntityType = EntityType
        };
        inputDeps = moveHoldHitJob.Schedule(moveHoldHitGroup, inputDeps);

        CommandBufferSystem.AddJobHandleForProducer(inputDeps);
        inputDeps.Complete();
    }

    [BurstCompile]
    private struct FrozenJob : IJobChunk
    {
        [WriteOnly] public EntityCommandBuffer.ParallelWriter CommandBuffer;

        [ReadOnly] public ComponentTypeHandle<ActionFrozenData> ActionFrozenDataType;
        [ReadOnly] public EntityTypeHandle EntityType;
        [ReadOnly] public double time;

        public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask,
            in v128 chunkEnabledMask)
        {
            var actionFrozenDataArray = chunk.GetNativeArray(ActionFrozenDataType);
            var entities = chunk.GetNativeArray(EntityType);

            for (var i = 0; i < chunk.Count; i++)
            {
                var actionFrozenData = actionFrozenDataArray[i];
                if (actionFrozenData.lastTime > time) continue;

                var entity = entities[i];
                CommandBuffer.RemoveComponent<ActionFrozenData>(unfilteredChunkIndex, entity);
            }
        }
    }

    [BurstCompile]
    private struct HoldJob : IJobChunk
    {
        [WriteOnly] public EntityCommandBuffer.ParallelWriter CommandBuffer;
        [ReadOnly] public ComponentTypeHandle<ActionHoldData> ActionHoldDataType;

        [ReadOnly] public EntityTypeHandle EntityType;

        public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask,
            in v128 chunkEnabledMask)
        {
            var entities = chunk.GetNativeArray(EntityType);
            var actionHoldDataArray = chunk.GetNativeArray(ActionHoldDataType);

            for (var i = 0; i < chunk.Count; i++)
            {
                var actionHoldData = actionHoldDataArray[i];
                if (!actionHoldData.auto) continue;
                var entity = entities[i];

                CommandBuffer.RemoveComponent<ActionHoldData>(unfilteredChunkIndex, entity);
            }
        }
    }

    [BurstCompile]
    private struct MoveHoldHitJob : IJobChunk
    {
        [WriteOnly] public EntityCommandBuffer.ParallelWriter CommandBuffer;
        [ReadOnly] public ComponentTypeHandle<MoveHoldData> MoveHoldDataType;
        [ReadOnly] public EntityTypeHandle EntityType;
        [ReadOnly] public BufferLookup<CastHitsData> CastHitsDataType;
        [ReadOnly] public ComponentLookup<EntityData> EntityDataFromEntity;

        public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask,
            in v128 chunkEnabledMask)
        {
            var moveHoldDataArray = chunk.GetNativeArray(MoveHoldDataType);
            var entities = chunk.GetNativeArray(EntityType);

            for (var i = 0; i < chunk.Count; i++)
            {
                var moveHoldData = moveHoldDataArray[i];
                if (!moveHoldData.close) continue;
                var entity = entities[i];
                var castHitsDataArray = CastHitsDataType[entity];
                var e = castHitsDataArray.GetEnumerator();
                while (e.MoveNext())
                {
                    var row = e.Current;
                    if (!EntityDataFromEntity.HasComponent(row.entity)) continue;
                    CommandBuffer.AddComponent(unfilteredChunkIndex, row.entity, new ActionHoldData());
                }

                e.Dispose();
                CommandBuffer.RemoveComponent<CastHitsData>(unfilteredChunkIndex, entity);
            }

            for (var i = 0; i < chunk.Count; i++)
            {
                var moveHoldData = moveHoldDataArray[i];
                if (moveHoldData.close) continue;
                var entity = entities[i];
                var castHitsDataArray = CastHitsDataType[entity];
                var e = castHitsDataArray.GetEnumerator();
                while (e.MoveNext())
                {
                    var row = e.Current;
                    if (!EntityDataFromEntity.HasComponent(row.entity)) continue;
                    CommandBuffer.RemoveComponent<ActionHoldData>(unfilteredChunkIndex, row.entity);
                }

                e.Dispose();
                CommandBuffer.RemoveComponent<CastHitsData>(unfilteredChunkIndex, entity);
            }
        }
    }
}